﻿namespace NServiceBus.AcceptanceTests.Core.Recoverability;

using System;
using System.Threading.Tasks;
using AcceptanceTesting;
using EndpointTemplates;
using Faults;
using NUnit.Framework;

public class When_subscribing_to_immediate_retries_notifications : NServiceBusAcceptanceTest
{
    [Test]
    public async Task Should_trigger_notification_on_immediate_retry()
    {
        var context = await Scenario.Define<Context>()
            .WithEndpoint<RetryingEndpoint>(b =>
            {
                b.DoNotFailOnErrorMessages();
                b.When((session, c) => session.SendLocal(new MessageToBeRetried()));
            })
            .Done(c => c.MessageSentToError)
            .Run();

        using (Assert.EnterMultipleScope())
        {
            Assert.That(context.LastImmediateRetryInfo.Exception, Is.InstanceOf<SimulatedException>());
            // Immediate Retries max retries = 3 means we will be processing 4 times. Delayed Retries max retries = 2 means we will do 3 * Immediate Retries
            Assert.That(context.TotalNumberOfHandlerInvocations, Is.EqualTo(4));
            Assert.That(context.TotalNumberOfImmediateRetriesEventInvocations, Is.EqualTo(3));
            Assert.That(context.LastImmediateRetryInfo.RetryAttempt, Is.EqualTo(2));
        }
    }

    class Context : ScenarioContext
    {
        public int TotalNumberOfImmediateRetriesEventInvocations { get; set; }
        public int TotalNumberOfHandlerInvocations { get; set; }
        public bool MessageSentToError { get; set; }
        public ImmediateRetryMessage LastImmediateRetryInfo { get; set; }
    }

    public class RetryingEndpoint : EndpointConfigurationBuilder
    {
        public RetryingEndpoint()
        {
            EndpointSetup<DefaultServer>((config, context) =>
            {
                var testContext = (Context)context.ScenarioContext;

                var recoverability = config.Recoverability();
                recoverability.Failed(f => f.OnMessageSentToErrorQueue((failedMessage, _) =>
                {
                    testContext.MessageSentToError = true;
                    return Task.CompletedTask;
                }));

                recoverability.Immediate(immediateRetriesSettings =>
                {
                    immediateRetriesSettings.NumberOfRetries(3);
                    immediateRetriesSettings.OnMessageBeingRetried((retryInfo, _) =>
                    {
                        testContext.TotalNumberOfImmediateRetriesEventInvocations++;
                        testContext.LastImmediateRetryInfo = retryInfo;
                        return Task.CompletedTask;
                    });
                });
            });
        }

        class MessageToBeRetriedHandler : IHandleMessages<MessageToBeRetried>
        {
            public MessageToBeRetriedHandler(Context testContext)
            {
                this.testContext = testContext;
            }

            public Task Handle(MessageToBeRetried message, IMessageHandlerContext context)
            {
                testContext.TotalNumberOfHandlerInvocations++;

                throw new SimulatedException("Simulated exception message");
            }

            Context testContext;
        }
    }

    public class MessageToBeRetried : IMessage
    {
        public Guid Id { get; set; }
    }
}